package com.infonova.product.jtf.blame;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import junit.framework.TestFailure;

import org.apache.bcel.classfile.ClassParser;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.IOFileFilter;
import org.apache.commons.lang.time.DateUtils;
import org.dom4j.DocumentException;
import org.dom4j.io.SAXReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.infonova.product.jtf.blame.callgraph.ClassMethodVisitor;
import com.infonova.product.jtf.blame.dto.Call;
import com.infonova.product.jtf.blame.dto.CallGraph;
import com.infonova.product.jtf.blame.dto.TestedMethod;
import com.infonova.product.jtf.blame.svn.Change;
import com.infonova.product.jtf.blame.svn.History;
import com.infonova.product.jtf.blame.util.JtfBlameUtil;
import com.infonova.product.jtf.blame.web.GraphRenderer;


public class JtfBlame {

    private Logger logger = LoggerFactory.getLogger(getClass());


    public static void main(String[] args) throws Exception {
        String testmoduleFileSystem = "E:/workspace/blame-test-failure/demo/demo-test";
        String svnUrl = "https://blame-test-failure.googlecode.com/svn/trunk/demo";
        String svnName = "christian.tassler@gmail.com";
        String svnPassword = "fm8EZ8DX3wV5";

        JtfBlame main = new JtfBlame();
        File target = new File("target/web");
        GraphRenderer render = new GraphRenderer(target );

        List<TestFailure> testFailures = main.loadReports(new File(testmoduleFileSystem));

        List<CallGraph> callGraphs = new ArrayList<CallGraph>();
        for (TestFailure failure : testFailures) {
            CallGraph callGraph = main.createCallGraph((TestedMethod)failure.failedTest());
            callGraphs.add(callGraph);
        }

        render.createOverviewPage(testFailures);

        Date from = DateUtils.addDays(new Date(), -2);
        from = DateUtils.addHours(new Date(), -6);
        Date to = new Date();
        History history = new History(svnUrl, svnName, svnPassword);
        List<Change> changes = history.retrieveChanges(from, to);

        for (CallGraph callGraph : callGraphs) {
            Map<String, List<Change>> changedFiles = main.getFileChanges(callGraph, changes);
            render.createCallGraph(callGraph, changedFiles);
        }
    }


    public List<TestFailure> loadReports(File reportContainer) throws IOException {
        List<TestFailure> testFailures = new ArrayList<TestFailure>();
        Collection<File> reportFiles = getReportFiles(reportContainer);
        for (File file : reportFiles) {
            TestReportResolver resolver = new TestReportResolver();
            try {
                SAXReader saxReader = new SAXReader();
                saxReader.setDefaultHandler(resolver);
                saxReader.read(file);
                logger.debug("Found {} failures in Testreport {}", resolver.getTestFailures().size(), file.getName());
                testFailures.addAll(resolver.getTestFailures());
            } catch (DocumentException e) {
                logger.error("Error parsing Testreport {}: {}", file.getName(), e.getMessage());
            }
        }
        logger.info("Found {} failing Tests in {} Report files", testFailures.size(), reportFiles.size());
        return testFailures;
    }


    @SuppressWarnings("unchecked")
    public Collection<File> getReportFiles(File reportContainer) {
        return FileUtils.listFiles(reportContainer, new IOFileFilter() {

            @Override
            public boolean accept(File file) {
                return accept(file, file.getName());
            }

            @Override
            public boolean accept(File file, String fileName) {
                return fileName.startsWith("TEST-com.infonova") && fileName.endsWith(".xml");
            }
        }, new IOFileFilter() {

            @Override
            public boolean accept(File arg0) {
                return true;
            }

            @Override
            public boolean accept(File arg0, String arg1) {
                return true;
            }
        });
    }


    public CallGraph createCallGraph(TestedMethod method)
            throws ClassNotFoundException, SecurityException, NoSuchMethodException, IOException {
        CallGraph callgraph = new CallGraph(method);
        logger.info("Create Callgraph for {}", method);

        ClassParser cp = new ClassParser(ClassFinder.getInstance().findSourceFile(method.getTestClazz()));
        ClassMethodVisitor visitor = new ClassMethodVisitor(0, cp.parse(), method.getTestMethod(), callgraph);
        visitor.start();
        return callgraph;
    }


    public Map<String, List<Change>> getFileChanges(CallGraph callGraph, List<Change> changes) {
        Map<String, List<Change>> filesToCheck = new HashMap<String, List<Change>>();
        for (Call call : callGraph.getCalls()) {
            String path = JtfBlameUtil.toPartPath(call.getClazzName());

            List<Change> fileChanges = new ArrayList<Change>();
            for (Change change : changes) {
                if (change.getPartFilePath().contains(path)) {
                    fileChanges.add(change);
                }
            }
            filesToCheck.put(path, fileChanges);
        }
        return filesToCheck;
    }


}
